import abc
import threading
from datetime import datetime
from typing import List, Dict, Union, Optional
import peewee

from emi import report
from emi.bar.data import Interval, BarData
from emi.bar.db import DayDB
from emi.core.DBSource import DBSource
from emi.util import TimeUtils
from emi.rdp.model import BarDataModel, TickShotModel
from emi.rdp.db import BarDataDB
from emi.rdp.db import TickShotDB,TodayTickShotDB

class RdpDayDB(DayDB):
    """
    该实例对象是短生命周期对象，占用远程数据库连接资源。
    1、空闲久了，远程数据库连接就会端开。
    """
    _LOCK = threading.RLock()
    _TODAY_BY_AUTO: datetime = None  ## 自动获取的最新天数
    _TODAY_BY_AUTO_CHECK_TIME: datetime = None

    def __init__(self,source:DBSource,tick_table_name:str = None,bar_table_name:str = None,day:datetime = None):
        self.db_source = source
        if tick_table_name:
            self.tick_today_db = TodayTickShotDB(source,table_name=tick_table_name)
        else:
            self.tick_today_db = TodayTickShotDB(source)
        if bar_table_name:
            self.bar_db = BarDataDB(source,table_name=bar_table_name)
        else:
            self.bar_db = BarDataDB(source)

        self._today_set_by_manual = day  ## 手动设置的天数




    def set_day(self,day:datetime):
        self._today_set_by_manual = day


    def refresh_today(self)->datetime:
        """
        刷新当前的天数
        """
        days = self.tick_today_db.get_day_list()
        if days:
            RdpDayDB._TODAY_BY_AUTO = days[-1]
            RdpDayDB._TODAY_BY_AUTO_CHECK_TIME = datetime.now()
            report.log_warn(
                f"【RdpDayDB】check latest day:{RdpDayDB._TODAY_BY_AUTO_CHECK_TIME},day:{RdpDayDB._TODAY_BY_AUTO}")
        return RdpDayDB._TODAY_BY_AUTO

    def get_day(self) -> datetime:
        """
        返回最新的实时天数。
        """
        if self._today_set_by_manual is not None:
            ### 优先使用手动设置的天数。
            return self._today_set_by_manual

        ## 自动获取最新天数
        _cur_time = datetime.now()

        is_same_day = _cur_time.date() == RdpDayDB._TODAY_BY_AUTO_CHECK_TIME.date() if RdpDayDB._TODAY_BY_AUTO_CHECK_TIME  else False
        if RdpDayDB._TODAY_BY_AUTO_CHECK_TIME is None or not is_same_day:
            with RdpDayDB._LOCK:
                is_same_day = _cur_time.date() == RdpDayDB._TODAY_BY_AUTO_CHECK_TIME.date() if RdpDayDB._TODAY_BY_AUTO_CHECK_TIME else False
                if RdpDayDB._TODAY_BY_AUTO_CHECK_TIME is None or not is_same_day:
                    self.refresh_today()
        return RdpDayDB._TODAY_BY_AUTO

    def get_day_list(self):
        return self.tick_today_db.get_day_list()

    def get_interval(self) -> Interval:
        return Interval.M1

    def get_symbol_list(self) -> List[str]:
        today = self.get_day()
        if today is None:
            return []
        return self.tick_today_db.get_code_list()

    def get_hhmmss_list(self) -> List[int]:
        today = self.get_day()
        if today is None:
            return []
        return self.bar_db.get_hhmm_list(today)

    def get_bars(self, symbol: str) -> List[BarData]:
        today = self.get_day()
        if today is None:
            return []
        models =  self.bar_db.get_bars(symbol, day=today)
        bars = [ self._to_bar(model,interval=Interval.M1) for model in models]
        return bars

    def get_bars_at_hhmm(self, hhmm: int) -> List[BarData]:
        today = self.get_day()
        if today is None:
            return []
        models = self.bar_db.get_bars_by_time(time=today, hh_mm=hhmm)
        bars = [self._to_bar(model, interval=self.get_interval()) for model in models]
        return bars

    def get_tick(self,symbol:str)->Optional[BarData]:
        today = self.get_day()
        if today is None:
            return None
        model = self.tick_today_db.get_today_tick(day=today, symbol=symbol)
        if model:
            return self._to_bar(model,interval=Interval.DAILY)
        return None

    def get_ticks(self, symbols: List[str]=None, as_map=False) -> Union[List[BarData], Dict[str, BarData]]:
        today = self.get_day()
        if today is None:
            return {} if as_map else []
        models = self.tick_today_db.get_today_ticks(day=today, symbols=symbols)
        bars = [ self._to_bar(model,interval=Interval.DAILY) for model in models]
        if as_map:
            _map_ret = {}
            for bar in bars:
                _map_ret[bar.symbol] = bar
            return _map_ret
        return bars

    def _to_bar(self,model:Union[BarDataModel,TickShotModel],interval:Interval)->BarData:
        bar = BarData(symbol=model.code,datetime=None,interval=interval)

        if isinstance(model,BarDataModel):
            bar.datetime = TimeUtils.merge_datetime(model.yy_mm_dd,model.hh_mm*100)
        else:
            bar.datetime = model.time
        bar.high = model.high
        bar.close = model.close
        bar.open = model.open
        bar.low = model.low
        bar.volume = model.volume
        bar.pre_close = model.pre_close
        bar.amount = model.amount
        return bar


    def clear_before_at_time(self,time:datetime):
        """
        删除以前的数据。
        :param time: 包括time当天的数据也会删除
        :return:
        """
        self.bar_db.clear_before(time)
        self.tick_today_db.clear_before(time)